package com.easylinkin.linkappapi.security.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.additional.update.impl.LambdaUpdateChainWrapper;
import com.easylinkin.bases.aliyun.AliyunSmsUtils;
import com.easylinkin.bases.redis.util.RedisUtil;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.utils.excel.ExcelReadUtil;
import com.easylinkin.linkappapi.gaodemap.util.GaodeUtils;
import com.easylinkin.linkappapi.lobar.dto.excel.ExcelResultDTO;
import com.easylinkin.linkappapi.lobar.dto.excel.ExcelResultDetailDTO;
import com.easylinkin.linkappapi.openapi.service.OpenApiService;
import com.easylinkin.linkappapi.ruleengine.service.RuleEngineService;
import com.easylinkin.linkappapi.security.constant.LinkappUserConstant;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.security.entity.LinkappRole;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import com.easylinkin.linkappapi.security.entity.LinkappUserRefRole;
import com.easylinkin.linkappapi.security.mapper.LinkappUserMapper;
import com.easylinkin.linkappapi.security.repository.LinkappUserRepository;
import com.easylinkin.linkappapi.security.service.LinkappRoleService;
import com.easylinkin.linkappapi.security.service.LinkappUserService;
import com.easylinkin.linkappapi.space.entity.LinkappSpace;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import com.easylinkin.sm.constant.UserConstant.Attach;
import com.easylinkin.sm.constant.UserConstant.Data;
import com.easylinkin.sm.constant.UserConstant.Error;
import com.easylinkin.sm.dto.ResetPasswordVo;
import com.easylinkin.sm.entity.User;
import com.easylinkin.sm.util.Cache;
import com.easylinkin.sm.util.DataPermissionUtils;
import com.easylinkin.sm.util.VerifyCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import site.morn.boot.data.DisplayableServiceSupport;
import site.morn.boot.data.entity.OrganizedEntity;
import site.morn.boot.jpa.JpaConditionSupport;
import site.morn.boot.jpa.SpecificationBuilder;
import site.morn.cache.CacheGroup;
import site.morn.core.CriteriaMap;
import site.morn.exception.ApplicationMessages;
import site.morn.framework.context.AccountContext;
import site.morn.framework.entity.BaseUser;
import site.morn.framework.entity.BaseUser.Fields;
import site.morn.log.OperateArguments;
import site.morn.rest.RestBuilders;
import site.morn.rest.RestMessage;
import site.morn.rest.RestModel;
import site.morn.util.GenericUtils;
import site.morn.util.MessageDigestUtils;
import site.morn.validate.persistent.PersistFunctionUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static com.easylinkin.sm.constant.UserConstant.Attach.FILTER_DEPARTMENT_ID;
import static site.morn.constant.DigestConstant.Algorithms.SPRING_B_CRYPT;
import static site.morn.framework.context.CommonConstant.Caches.USER;

//import com.easylinkin.linkappapi.alarm.service.AlarmRuleService;
//import com.easylinkin.linkappapi.intelligentrule.service.IntelligentRuleService;

//import com.easylinkin.linkappapi.linkage.service.IntelligentTaskService;
//import com.easylinkin.linkappapi.linkage.service.LinkageConfigService;


/**
 * 用户服务
 *
 * @author TianGanLin
 * @since 1.0.0, 2017/9/28
 */
@Slf4j
@Service
@Component
@Transactional
@CacheConfig(cacheNames = {"getCode"})
public class LinkappUserServiceImpl extends
    DisplayableServiceSupport<LinkappUser, Long, LinkappUserRepository> implements LinkappUserService {

    @Resource
    protected LinkappUserMapper linkappUserMapper;

    @Resource
    protected Cache cache;
    @Resource
    private OpenApiService openApiService;

    @Resource
    LinkappUserContextProducer linkappUserContextProducer;

    @Resource
    LinkappTenantService linkappTenantService;

    @Resource
    private RedisUtil redisUtil;

//    @Resource
//    AlarmRuleService alarmRuleService;

//    @Resource
//    IntelligentRuleService intelligentTaskService;

    @Resource
    RuleEngineService ruleEngineService;

    @Resource
    private LinkappRoleService linkappRoleService;

//    @Override
//    public LinkappUser get2(Long id) {
//        Assert.notNull(id, Error.USER_NUMBER_NOT_NULL);
//        return repository().findById(id).orElse(null);
//        return repository()
//            .findOne(SpecificationBuilder.specification((root, query, builder, predicate) -> {
//                Predicate equal = builder.equal(root.get(Fields.id), id);
//                root.fetch(LinkappUser.Fields.roles); // 拉取角色信息，注意：用户未关联角色时，该用户也无法查询
//                predicate.appendAnd(equal);
//            })).orElse(null);

//    }

    @Override
    public LinkappUser getCurrentTenantAdmin() {
        LinkappUser current = linkappUserContextProducer.getNotNullCurrent();
        return getCurrentTenantAdmin(current.getTenantId());
    }

    @Override
    public LinkappUser getCurrentTenantAdmin(String tenantId) {
        QueryWrapper<LinkappUser> qw = new QueryWrapper<>();
        qw.eq("tenant_id", tenantId);
        qw.eq("type", LinkappUserConstant.ADMIN_TYPE);
        List<LinkappUser> list = linkappUserMapper.selectList(qw);
        if (ObjectUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public LinkappUser findByUsername(String username) {
        if (username == null) {
            return null;
        }
        Object obj = AccountContext.cacheGroup().get(USER, username);
        if (obj != null) {
            return (LinkappUser) obj;
        } else {
            AccountContext.cacheGroup().clear(USER, username);
            QueryWrapper<LinkappUser> wrapper = new QueryWrapper<>();
            wrapper.eq("username", username);
            wrapper.orderByAsc("create_time");
            LinkappUser linkappUser = linkappUserMapper.selectOne(wrapper);
            if (linkappUser != null) {
                AccountContext.cacheGroup().put(USER, username, linkappUser);
            }
            return linkappUser;
        }
    }

    @Override
    public LinkappUser findAdminUserByProjectId(String projectId) {
        if (projectId == null) {
            return null;
        }
        QueryWrapper<LinkappTenant> wrapper1 = new QueryWrapper<>();
        wrapper1.eq("project_id", projectId);
        wrapper1.select("id");
        List<LinkappTenant> tenants = linkappTenantService.getBaseMapper().selectList(wrapper1);
        if (!tenants.isEmpty()) {
            LinkappTenant tenant = tenants.get(0);
            QueryWrapper<LinkappUser> wrapper = new QueryWrapper<>();
            wrapper.eq("tenant_id", tenant.getId());
            wrapper.eq("type", "1");
            wrapper.select("username");
            List<LinkappUser> linkappUsers = linkappUserMapper.selectList(wrapper);
            if (!linkappUsers.isEmpty()) {
                return linkappUsers.get(0);
            }
        }
        return null;
    }

    @Override
    public List<LinkappUser> findByUsernameBatch(List<String> userNameList){
        QueryWrapper qw = new QueryWrapper();
        qw.in("nickname", userNameList);
        List<LinkappUser> list = linkappUserMapper.selectList(qw);
        return list;
    }

    @Override
    public void resetPassword(ResetPasswordVo vo) {
        User manager = AccountContext.currentUser();
        // 管理员密码错误
        if (!MessageDigestUtils
            .matches(SPRING_B_CRYPT, vo.getPasswordAuthentication(), manager.getPassword())) {
            throw ApplicationMessages.translateException(Error.MANAGER_SECRET_IS_WRONG);
        }
        // 获取用户信息
        LinkappUser user = super.get(vo.getId());
        if (Objects.isNull(user)) {
            throw ApplicationMessages.translateException(Error.USER_NO_FOUND, vo.getId());
        }
        updatePassword(vo, user);
        // 重置密码处理
        //BeanFunctionUtils.processes(UserResetProcessor.class, user, Functions.TAG_RESET);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void tenantLock(List<String> tenantIdList) {
        int size = tenantIdList.size();
        int baseNum = 50;
        int sunSum = GaodeUtils.getLoopCount(size, baseNum);
        int listStart,listEnd;
        for (int i = 1; i <= sunSum; i++) {
            listStart = (i - 1) * baseNum;
            listEnd = listStart + baseNum;
            if (i == sunSum) {
                listEnd = size;
            }
            List<String> subList = tenantIdList.subList(listStart, listEnd);
            linkappUserMapper.tenantLockBatch(subList);
            ruleEngineService.disableByTenantIdBatch(subList);
        }
    	/*for(String tenantId:tenantIds) {
    		linkappUserMapper.tenantLock(tenantId);
//    	    alarmRuleService.disableByTenantId(tenantId);
//    	    intelligentTaskService.disableByTenantId(tenantId);
    	    ruleEngineService.disableByTenantId(tenantId);
    	}*/
    }



    @Override
    public void updatePassword(ResetPasswordVo vo) {
        // 获取用户信息
    	LinkappUser user = super.get(vo.getId());
        if (Objects.isNull(user)) {
            throw ApplicationMessages.translateException(Error.USER_NO_FOUND, vo.getId());
        }
        // 旧密码错误
        if (!MessageDigestUtils
            .matches(SPRING_B_CRYPT, vo.getPasswordAuthentication(), user.getPassword())) {
            throw ApplicationMessages.translateException(Error.ORIGINAL_SECRET_IS_WRONG);
        }
        updatePassword(vo, user);
    }

    @Override
    public LinkappUser updateProfile(LinkappUser user) {
        Assert.notNull(user, Error.USER_NUMBER_NOT_NULL);
        Long id = user.getId();
        Assert.notNull(id, Error.USER_NUMBER_NOT_NULL);
//        LinkappUser persist = get(id);
        LinkappUser persist = repository().findById(id).orElse(null);
        Assert.notNull(persist, Error.USER_NUMBER_NOT_NULL);
        // 修改个人信息不允许影响角色
        user.setRoles(persist.getRoles());
        user.setType(persist.getType());
        user.setTenantId(persist.getTenantId());
        // 删除缓存 管理员类型用户名
        AccountContext.cacheGroup().clear("findAdminTypeUserNameByTenantId", persist.getTenantId());
        return update(user);
    }

    @Override
    public Boolean updateAccount(String account,String newAccount) {
    	try {
    		LinkappUser user = linkappUserContextProducer.getCurrent();
    		LinkappTenant tenant = new LinkappTenant();
    		tenant.setId(user.getTenantId());
        	tenant = linkappTenantService.selectLinkappTenant(tenant);
			if(openApiService.updateTenantAccount(tenant, account, newAccount)) {
				user.setUsername(newAccount);
                linkappUserMapper.updateById(user);
                // 删除缓存 管理员类型用户名
                AccountContext.cacheGroup().clear("findAdminTypeUserNameByTenantId", user.getTenantId());
				return true;
			}else {
				return false;
			}
		} catch (UnsupportedEncodingException e) {
			return false;
		}
    }



    @Override
    public boolean existsByRole(Long roleId) {
        Assert.notNull(roleId, "角色编号不能为空");
        long count = repository().countByRolesId(roleId);
        return count > 0;
    }

    @Override
    public <S extends LinkappUser> S unlock(Long id) {
        S s = toggleLock(id, false);
//        此操作会导致 解锁后用户关联角色被清空
//        userLoginInfoService.clearErrorCount(id);
        return s;
    }

    @Override
    public <S extends LinkappUser> S toggleLock(Long id, boolean isLock) {
        Assert.notNull(id, "参数id不能为空");
        LinkappUser linkappUser = repository().findById(id).orElse(null);
        Assert.notNull(linkappUser, "找不到该用户");
        if (Objects.nonNull(linkappUser)) {
            linkappUser.setLocked(isLock);
            update(linkappUser);
        }
        return GenericUtils.castFrom(linkappUser);
    }

    @Override
    public <S extends LinkappUser> S add(RestModel<S> restModel) {
        S model = restModel.getModel();
        initModel(model);
        OperateArguments.add(model.getUsername());
        validateModel(model); // 校验重复项
        return repository().save(model);
    }

    @Override
    public <S extends LinkappUser> S update(S model) {
        Assert.notNull(model.getId(), "用户编号不能为空");
        OperateArguments.add(model.getUsername());
        validateModel(model); // 校验重复项
        LinkappUser persist = repository().findById(model.getId()).orElse(null);
        // 更新用户不允许影响密码,租户id,角色
        if (Objects.nonNull(persist)) {
            model.setPassword(persist.getPassword());
            model.setTenantId(persist.getTenantId());
            model.setRoles(persist.getRoles());
        }
        super.update(model);
        // 更新用户缓存
        model.setRoles(Collections.emptyList());
        CacheGroup cacheGroup = AccountContext.cacheGroup(); // 角色单独缓存
        cacheGroup.put(USER, model.getUsername(), model);
        return model;
    }

    @Override
    public void delete(Long id) {
        Optional<LinkappUser> optional = this.repository().findById(id);
        if (optional.isPresent()) {
            PersistFunctionUtils.validateDelete(optional.get());
            OperateArguments.add(optional.get().getUsername());
            repository().deleteById(id);
        } else {
            log.warn("数据不存在：[id={}]", id);
        }
    }


    @Override
    protected Specification<LinkappUser> searchSpecification(LinkappUser model, CriteriaMap attach) {
        Long departmentId = attach.getLong(FILTER_DEPARTMENT_ID);
        //如果未选组织树，则机构为当前用户的机构
        if (null == departmentId || 0 == departmentId) {
            BaseUser baseUser = AccountContext.currentUser();
            departmentId = baseUser.getDepartmentId();
        }
        return SpecificationBuilder.withParameter(model, attach)
                .specification((reference, predicate, condition) -> {
                    CriteriaBuilder builder = reference.builder();
                    User currentUser = AccountContext.currentUser(); // 当前登录用户
                    // 过滤当前用户
                    JpaConditionSupport<User> conditionSupport = (JpaConditionSupport<User>) condition;
                    Predicate filterCurrent = conditionSupport.innerBuilder()
                            .mapPredicate(Fields.username, currentUser.getUsername(), builder::notEqual);
                    // 按组织机构匹配
                    Predicate department = condition.eq(OrganizedEntity.Fields.departmentId);
                    // 按用户名/姓名模糊搜索
                    String[] searchAttributes = {Fields.username, Fields.nickname};
                    Predicate[] containsKeyword = condition.contains(searchAttributes, Attach.KEYWORD);
                    predicate.appendAnd(filterCurrent, department, predicate.mergeOr(containsKeyword));
                }).and(DataPermissionUtils.subOrganizations(departmentId))
                .and(DataPermissionUtils.display());
    }

    /**
     * 初始化数据模型
     *
     * @param model 数据模型
     */
    private void initModel(LinkappUser model) {
        //密码加密
        model.setPassword(MessageDigestUtils.encrypt(SPRING_B_CRYPT, model.getPassword()));
        model.setDisplay(true);
        model.setLocked(model.getLocked());
    }

    /**
     * 校验重复项
     *
     * @param model 数据模型
     */
    private void validateModel(LinkappUser model) {
        LinkappUser user = sameOne(model);
        if (Objects.isNull(user)) {
            return;
        }
        if (Objects.equals(user.getUsername(), model.getUsername())) {
            throw ApplicationMessages.translateException(Error.ACCOUNT_REPEAT);
        }
    }

    /**
     * 查询名称相同的数据
     *
     * @param model 数据模型
     * @return 实例
     */
    private LinkappUser sameOne(LinkappUser model) {
        Optional<LinkappUser> optional = repository().findOne(SpecificationBuilder.withParameter(model)
                .specification((reference, predicate, condition) -> {
                    Predicate id = condition.notEqual(Fields.id);
                    CriteriaBuilder builder = reference.builder();
                    Root root = reference.root();
                    Predicate username = builder.equal(root.get(Fields.username), model.getUsername());
                    Predicate display = builder.equal(root.get(Data.DISPLAY), true);
                    predicate.appendAnd(id, username, display);
                }));
        return optional.orElse(null);
    }

    /**
     * 修改密码
     */
    private void updatePassword(ResetPasswordVo vo, LinkappUser user) {
        // 两次密码不一致
        String password = vo.getPassword();
        if (!Objects.equals(password, vo.getPasswordConfirm())) {
            throw ApplicationMessages.translateException(Error.SECRET_CONFIRM_FAILURE);
        }
        userCheck(password, user);

        Long id = user.getId();
        Assert.notNull(id, Error.USER_NUMBER_NOT_NULL);
        LinkappUser persist = get(id);
        // 修改个人信息不允许影响角色
        if (Objects.nonNull(persist)) {
            user.setRoles(persist.getRoles());
            user.setType(persist.getType());
            user.setTenantId(persist.getTenantId());
        }
        repository().save(user);
    }

    /**
     * 修改密码,重置密码公共代码
     */
    private void userCheck(String password, LinkappUser user) {
        // 获取用户信息
        if (Objects.isNull(user)) {
            throw ApplicationMessages.translateException(Error.FORGET_USER_NO_FOUND);
        }
        // 新密码与旧密码一样
        if (MessageDigestUtils.matches(SPRING_B_CRYPT, password, user.getPassword())) {
            throw ApplicationMessages.translateException(Error.SECRET_REPEAT);
        }
        // 使用BCrypt加密
        String passwordEncoded = MessageDigestUtils.encrypt(SPRING_B_CRYPT, password);
        user.setPassword(passwordEncoded);
        user.setLocked(user.getLocked());
    }

    /**
     * 忘记密码，重置密码
     */
    @Override
    public void forgetPassword(String username, String password) {
        LinkappUser user = this.findByUsername(username);
        //20220622变更:批量修改用户手机号在各个tenant下的用户的密码
        if (user == null){
            throw ApplicationMessages.translateException(Error.FORGET_USER_NO_FOUND);
        }
        String phone = user.getPhone();
        List<LinkappUser> userList = new ArrayList<>();
        userList.add(user);
        if (org.apache.commons.lang3.StringUtils.isNotBlank(phone)){
            //根据手机号查询所有用户
            LinkappUser tmpUser = new LinkappUser();
            tmpUser.setPhone(phone);
            List<LinkappUser> phoneUserLs = linkappUserMapper.selectUsers(tmpUser);
            userList.addAll(phoneUserLs);
        }
        batchResetPassword(userList,password);

        /*原单个修改密码逻辑
        userCheck(password, user);
        Long id = user.getId();
        Assert.notNull(id, Error.USER_NUMBER_NOT_NULL);
        LinkappUser persist = get(id);
        // 修改个人信息不允许影响角色
        if (Objects.nonNull(persist)) {
            user.setRoles(persist.getRoles());
            user.setType(persist.getType());
            user.setTenantId(persist.getTenantId());
        }
        repository().save(user);
        //删除缓存中的验证码
        cache.deleteSession();
        Object obj = AccountContext.cacheGroup().get(USER, username);
        if(obj != null){
            AccountContext.cacheGroup().clear(USER, username);
        }*/
    }

    /**
     * 批量重设密码
     *
     * @param userList 用户list
     * @param password 统一密码
     */
    private void batchResetPassword(List<LinkappUser> userList, String password) {
        if (CollectionUtil.isNotEmpty(userList)){
            userList.stream().forEach(c->{
                String username = c.getUsername();
                LinkappUser user = this.findByUsername(username);
                if (user != null){
                    // 使用BCrypt加密
                    String passwordEncoded = MessageDigestUtils.encrypt(SPRING_B_CRYPT, password);
                    user.setPassword(passwordEncoded);
                    user.setLocked(c.getLocked());
                    Long id = user.getId();
                    if (id != null){
                        LinkappUser persist = get(id);
                        // 修改个人信息不允许影响角色
                        if (Objects.nonNull(persist)) {
                            user.setRoles(persist.getRoles());
                            user.setType(persist.getType());
                            user.setTenantId(persist.getTenantId());
                        }
                    }
                    repository().save(user);
                    //删除缓存中的验证码
                    cache.deleteSession();
                    Object obj = AccountContext.cacheGroup().get(USER, user.getUsername());
                    if(obj != null){
                        AccountContext.cacheGroup().clear(USER, user.getUsername());
                    }
                }
            });
        }
    }

    @Override
    public String sendVerificationCode(String username, String phone, String imageVode) {
        String code;
        //阿里云短信服务模板，后期可以根据实际需求传值
        String templateCode = "SMS_174020720";
        if (!"".equals(username) && !"".equals(phone) && !"".equals(imageVode)) {
            LinkappUser user = findByUsername(username);
            if (Objects.isNull(user)) {
                throw ApplicationMessages.translateException(Error.FORGET_USER_NO_FOUND);
            }
            if (!phone.equals(user.getPhone())) {
                throw ApplicationMessages.translateException(Error.USERNAME_NOT_MATCH_PHONE).exception();
            }
            if (cache.setImageCode().equalsIgnoreCase(imageVode)) {
                cache.deleteSession();
                code = cache.getCode();
                //短信发送接口
                try {
                    SendSmsResponse sendSmsResponse = AliyunSmsUtils.sendSms(phone, code, templateCode);
                } catch (ClientException e) {
                    throw ApplicationMessages.translateException(Error.CAPTCHA_ACQUISITION_FAILED)
                            .exception();
                }
            } else {
                throw ApplicationMessages.translateException(Error.IMAGE_VERIFICATION_CODE_NOT_MATCH)
                        .exception();
            }
        } else {
            throw ApplicationMessages.translateException(Error.PLEASE_ENTER_USER_INFORMATION).exception();
        }
        return code;
    }


    @Override
    public boolean verificationMessages(String username, String phone, String imageVode,
                                        String code) {
        if (!(StringUtils.isEmpty(username) && StringUtils.isEmpty(phone) && StringUtils
                .isEmpty(imageVode) && StringUtils.isEmpty(code))) {
            LinkappUser user = findByUsername(username);
            if (Objects.isNull(user)) {
                throw ApplicationMessages.translateException(Error.FORGET_USER_NO_FOUND);
            }
            if (!phone.equals(user.getPhone())) {
                throw ApplicationMessages.translateException(Error.USERNAME_NOT_MATCH_PHONE).exception();
            }
            if (!cache.setImageCode().equalsIgnoreCase(imageVode)) {
                throw ApplicationMessages.translateException(Error.IMAGE_VERIFICATION_CODE_NOT_MATCH)
                        .exception();
            }
            if (!cache.getCode().equals(code)) {
                throw ApplicationMessages.translateException(Error.SMS_VERIFICATION_CODE_NOT_MATCH)
                        .exception();
            }
        } else {
            throw ApplicationMessages.translateException(Error.PLEASE_ENTER_USER_INFORMATION).exception();
        }
        return true;
    }

    @Override
    public String getImage() {
        String pngBase64 = null;
        //删除缓存
        cache.deleteImageCode();
        //存入缓存
        cache.setImageCode();
        //生成图片
        int width = 100;//宽
        int height = 35;//高
        try {
            pngBase64 = VerifyCodeUtils.outputImage(width, height, cache.setImageCode());
        } catch (IOException e) {
            throw ApplicationMessages.translateException(Error.IMG_NOT_FOUND).exception();
        }
        return pngBase64;
    }

    @Override
    public Integer auditUser(String date, String dateExp, String tenantId) {
        return linkappUserMapper.auditUser(date, dateExp, tenantId);
    }

    @Override
    public List<LinkappUser> selectUsersSorted(LinkappUser user) {
        return linkappUserMapper.selectUsersSorted(user);
    }

    @Override
    public List<LinkappUser> selectUsers(LinkappUser user) {
        return linkappUserMapper.selectUsers(user);
    }

    @Override
    public IPage<LinkappUser> selectUsersPage(Page page, LinkappUser user) {
        String tenantId = user.getTenantId();
        if(StringUtils.isEmpty(tenantId)){
            tenantId = linkappUserContextProducer.getCurrent().getTenantId();
        }
        user.setTenantId(tenantId);
        List<LinkappUser> list = linkappUserMapper.selectUsersSorted(page, user);
        //关联角色查询
        list.stream().forEach(item->
           filterUserRoles(item)
        );
        page.setRecords(list);
        return page;
    }

    private void filterUserRoles(LinkappUser user) {
        //根据id查询关联的角色
        List<LinkappRole> roles = linkappRoleService.selectRolesByUser(user);
        user.setRoles(roles);
    }

    @Override
    public List<LinkappSpace> selectCurrentUserSpace(Long id) {
        return linkappUserMapper.selectCurrentUserSpace(id);
    }

    @Override
    public void user2Roles(LinkappUser user, List<LinkappRole> roles) {
        linkappUserMapper.deleteUser2Roles(user.getId());
        for (LinkappRole role : roles) {
            LinkappUserRefRole userRefRole = new LinkappUserRefRole();
            userRefRole.setUserId(user.getId().toString());
            userRefRole.setTenantId(user.getTenantId());
            userRefRole.setRoleId(role.getId().toString());
            userRefRole.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            linkappUserMapper.insertUser2Roles(userRefRole);
        }
        //添加用户的时候，如果有绑定角色，更新这个缓存
        AccountContext.cacheGroup().clearGroup("space");
    }

    @Override
    public IPage<LinkappUser> selectUserByRolePage(Page page, LinkappRole role) {
        String tenantId = linkappUserContextProducer.getCurrent().getTenantId();
        role.setTenantId(tenantId);
        List<LinkappUser> list = linkappUserMapper.selectUserByRolePage(page, role);
        page.setRecords(list);
        return page;
    }

    @Override
    public List<LinkappUser> selectUserByRole(LinkappRole role) {
        return linkappUserMapper.selectUserByRolePage(role);
    }

    /***
     * 修改用户大屏图标显示
     * @param user
     */
    @Override
    public void updateUserIsShowScreen(LinkappUser user) {
        Assert.notNull(user, Error.USER_NUMBER_NOT_NULL);
        Long id = user.getId();
        Assert.notNull(id, Error.USER_NUMBER_NOT_NULL);
        LinkappUser persist = get(id);
        if (StringUtils.isEmpty(persist)) {
            Assert.notNull(id, Error.USER_NO_FOUND);
        }
        new LambdaUpdateChainWrapper<>(linkappUserMapper)
                .set(LinkappUser::getIsShowScreen, user.getIsShowScreen())
                .eq(LinkappUser::getId, user.getId())
                .update();
    }

    @Override
    public String findAdminTypeUserNameByTenantId(String tenantId) {

        Object obj = AccountContext.cacheGroup().get("findAdminTypeUserNameByTenantId", tenantId, () -> {
            QueryWrapper<LinkappUser> qw = new QueryWrapper<>();
            qw.eq("tenant_id", tenantId);
            qw.eq("type", LinkappUserConstant.ADMIN_TYPE);
            qw.select("username");
            List<LinkappUser> list = linkappUserMapper.selectList(qw);
            if (list.size() > 0) {
                return list.get(0).getUsername();
            }
            return null;
        });

        if (obj != null) {
            return obj.toString();
        } else {
            return null;
        }
    }

    @Override
    public List<LinkappUser> findAdminTypeUserNameByTenantIds(List<String> tenantIds) {
            QueryWrapper<LinkappUser> qw = new QueryWrapper<>();
            qw.in("tenant_id", tenantIds);
            qw.eq("type", LinkappUserConstant.ADMIN_TYPE);
            qw.select("id","username","tenant_id");
            List<LinkappUser> list = linkappUserMapper.selectList(qw);
            return list;
    }

    @Override
    public void validRepeat(LinkappUser user) {
        QueryWrapper<LinkappUser> qw = new QueryWrapper<>();
        qw.eq("tenant_id", user.getTenantId());
        qw.eq("phone", user.getPhone());

        List<LinkappUser> list = linkappUserMapper.selectList(qw);
        if (list.size() == 0) {
            return;
        }
        String msg = "检查此手机号在此租户下已存在";
        if (list.size() > 1) {
            throw new BusinessException(msg);
        } else if (user.getId() == null) {
            throw new BusinessException(msg);
        } else if (!user.getId().equals(list.get(0).getId())) {
            throw new BusinessException(msg);
        }
    }

    @Override
    public RestMessage resetPasswordByVerificationCode(String phone, String password,
        String verificationCode) {
        Assert.isTrue(org.apache.commons.lang3.StringUtils.isNotBlank(phone),"手机号为空");
        Assert.isTrue(org.apache.commons.lang3.StringUtils.isNotBlank(verificationCode),"验证码为空");

        Object obj  = redisUtil.get(phone);
        Assert.notNull(obj,"验证码失效或不存在");

        if (!verificationCode.equals(obj.toString())){
            return RestBuilders.failureBuilder().code("verificationCode.not.match")
                .message("验证码不匹配").build();

        }

        //根据手机号查询所有用户
        LinkappUser tmpUser = new LinkappUser();
        tmpUser.setPhone(phone);
        List<LinkappUser> phoneUserLs = linkappUserMapper.selectUsers(tmpUser);
        Assert.isTrue(CollectionUtil.isNotEmpty(phoneUserLs),"手机号用户不存在");

        batchResetPassword(phoneUserLs,password);
        return RestBuilders.successMessage();
    }

    @Override
    public List<LinkappUser> selectUserByPrivilegeCode(String privilegeCode, List<String> tenantIds) {
        return linkappUserMapper.selectUserByPrivilegeCode(privilegeCode, tenantIds);
    }

    @Override
    public List<LinkappUser> findByPhone(String phone) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("phone",phone);
        queryWrapper.isNotNull("registration_id");
        List<LinkappUser> list = linkappUserMapper.selectList(queryWrapper);
        return list;
    }

    @Override
    public LinkappUser findById(Long id) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("id",id);
        return linkappUserMapper.selectOne(queryWrapper);
    }

    @Override
    public void updateRegistrationId(LinkappUser appUser) {
        Assert.notNull(appUser.getPhone(),"手机号不能为空");
        Assert.notNull(appUser.getRegistrationId(),"registrationId不能为空");
        //先清除掉这个registrationId的绑定关系，避免一个手机登录前后登录多个用户的问题
        UpdateWrapper<LinkappUser> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("registration_id",null);
        updateWrapper.eq("registration_id",appUser.getRegistrationId());
        linkappUserMapper.update(null,updateWrapper);
        //重新绑定关联关系
        UpdateWrapper<LinkappUser> updateWrapper2 = new UpdateWrapper<>();
        updateWrapper2.set("registration_id",appUser.getRegistrationId());
        updateWrapper2.eq("phone",appUser.getPhone());
        linkappUserMapper.update(null,updateWrapper2);
    }

    @Override
    public ExcelResultDTO importExcel(MultipartFile file, Integer type) throws Exception {
        List<List<String>> excelList = ExcelReadUtil.getExcelInfo(file, 2, -1, -1);
        if (excelList.size() > 0 && excelList.get(0).size() < 5){
            throw new BusinessException("模板错误,请选择正确的文件导入");
        }
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        //创建一个Set保存excel本身的身份证号码
        CompletableFuture<ExcelResultDTO> completableFuture = CompletableFuture
            .supplyAsync(() -> getExcelResultDTO(excelList, type),executorService);
        ExcelResultDTO excelResultDTO = completableFuture.get();
        return excelResultDTO;
    }

    @Override
    public Map<String, LinkappUser> mapUserByIds(Collection<String> ids) {
        if(CollUtil.isEmpty(ids)){
            return null;
        }
        List<LinkappUser> userList = linkappUserMapper.selectList(Wrappers.<LinkappUser>lambdaQuery().in(LinkappUser::getId, ids));
        return userList.stream().collect(Collectors.toMap(o -> o.getId().toString(), o -> o, (key1, key2) -> key2));
    }

    /**
     * 执行导入和验证
     * @param type
     * @param excelList
     * @return
     */
    private ExcelResultDTO getExcelResultDTO(List<List<String>> excelList, Integer type) {
        ExcelResultDTO excelResultDTO = new ExcelResultDTO();
        //异常数据组织返回
        List<ExcelResultDetailDTO> excelResultDetailDTOS = new ArrayList<>();
        //成功数据
        //重复数据
        //失败数据
        List<LinkappUser> linkappUsers = new ArrayList<>();

        //获取当前租户下的所有用户
        LinkappUser currentUser = linkappUserContextProducer.getNotNullCurrent();
        String tenantId = currentUser.getTenantId();
        QueryWrapper<LinkappUser> qw = new QueryWrapper<>();
        qw.eq("tenant_id", tenantId);
        List<LinkappUser> currentAllUsers = linkappUserMapper.selectList(qw);

        //获取当前租户下的所有角色
        LinkappRole role = new LinkappRole();
        role.setTenantId(tenantId);
        List<LinkappRole> roles = linkappRoleService.findRoles(role);
        if(CollectionUtil.isEmpty(roles)){
            throw new BusinessException("当前租户下的没有角色");
        }

        Set<String> phoneSet = new HashSet<>();
        for (int i = 0; i < excelList.size(); i++) {
            ExcelResultDetailDTO excelResultDetailDTO = new ExcelResultDetailDTO();
            // 异常提示信息
            List<String> msgs = new ArrayList<>();
            List<String> list = excelList.get(i);
          List<String> collect = list.stream().filter(p -> StringUtils.isEmpty(p))
              .collect(Collectors.toList());
          if (CollectionUtil.isNotEmpty(list) && collect.size() > 4) {
            continue;
          }
            // 验证塞数据
            LinkappUser linkappUser = new LinkappUser();
            // 姓名校验
            linkappUser.setNickname(getNickname(list.get(0).trim(),msgs));
            // 手机号
            linkappUser.setPhone(getPhone(list.get(1).trim(),msgs,phoneSet));
            // 角色列表
            linkappUser.setRoles(getRoles(list.get(2).trim(),msgs,roles));
            // 公司
            if (!StringUtils.isEmpty(list.get(3).trim())){
                linkappUser.setAddress(getAddress(list.get(3).trim(),msgs));
            }
            // 性别
            if (!StringUtils.isEmpty(list.get(4).trim())){
                linkappUser.setSex(getSex(list.get(4).trim(),msgs));
            }
            excelResultDetailDTO.setNo(i+3);
            excelResultDetailDTO.setName(linkappUser.getNickname());
            // 失败数据
            if (msgs.size() > 0){
                excelResultDetailDTO.setDetail(org.apache.commons.lang3.StringUtils.join(msgs,","));
                excelResultDetailDTO.setType(1);
                excelResultDetailDTO.setTypeName("失败");
            }else {
                // 重复数据
                // 查询用户是否重复
                if(CollectionUtil.isNotEmpty(currentAllUsers)){
                    long count = currentAllUsers.stream().filter(c -> c.getPhone()!=null && c.getPhone().equals(linkappUser.getPhone())
                       ).count();
                    if (count > 0){
                        if (!Integer.valueOf(1).equals(type)){
                            excelResultDetailDTO.setDetail("系统已有相应账号，不再重复导入");
                            excelResultDetailDTO.setType(2);
                            excelResultDetailDTO.setTypeName("重复");
                            excelResultDetailDTOS.add(excelResultDetailDTO);
                            continue;
                        }else {
                            ExcelResultDetailDTO repeat = new ExcelResultDetailDTO();
                            repeat.setName(excelResultDetailDTO.getName());
                            repeat.setNo(excelResultDetailDTO.getNo());
                            repeat.setDetail("账号信息覆盖");
                            repeat.setType(2);
                            repeat.setTypeName("覆盖");
                            excelResultDetailDTOS.add(repeat);
                        }
                    }
                }
                linkappUsers.add(linkappUser);
            }
            excelResultDetailDTOS.add(excelResultDetailDTO);
        }
        //汇总
        excelResultDTO.setSum(excelList.size());
        excelResultDTO.setSuccess(linkappUsers.size());
        List<ExcelResultDetailDTO> repeat = excelResultDetailDTOS.stream()
            .filter(e -> Integer.valueOf(2).equals(e.getType())).collect(Collectors.toList());
        excelResultDTO.setRepeat(repeat.size());
        List<ExcelResultDetailDTO> fail = excelResultDetailDTOS.stream()
            .filter(e -> Integer.valueOf(1).equals(e.getType())).collect(Collectors.toList());
        excelResultDTO.setFail(fail.size());
        //去掉类型为空的
        excelResultDetailDTOS.removeIf(e-> null == e.getType());
        excelResultDTO.setExcelResultDetailDTOS(excelResultDetailDTOS);

        //保存数据
        insertList(linkappUsers,type,currentAllUsers);
        return excelResultDTO;
    }

    //保存数据
    private void insertList(List<LinkappUser> linkappUsers, Integer type, List<LinkappUser> currentAllUsers) {
        LinkappUser currentUser = linkappUserContextProducer.getNotNullCurrent();
        linkappUsers.forEach(l->{
            List<LinkappUser> userList = currentAllUsers.stream().filter(a -> a.getPhone()!=null && a.getPhone().equals(l.getPhone()))
                .collect(Collectors.toList());
            // 如果是软删除更新其状态
            if (userList.size() > 0){
                LinkappUser linkappUser = userList.get(0);
                // 资源池
                if (Integer.valueOf(1).equals(type)){
                    UpdateWrapper<LinkappUser> cUpdateWrapper = new UpdateWrapper<>();
                    cUpdateWrapper.set("nickname",l.getNickname())
                        .set("address",l.getAddress())
                        .set("sex",l.getSex())
                        .set("locked",0)
                        .set("create_time",new Date())
                        .set("creator",currentUser.getUsername())
                        .set("modify_time",new Date())
                        .set("modifier",currentUser.getUsername())
                        .eq("id",linkappUser.getId());
                    linkappUserMapper.update(null,cUpdateWrapper);
                    // 保存角色
                    linkappUser.setRoles(l.getRoles());
                    user2Roles(linkappUser,linkappUser.getRoles());
                }
            }else {
                l.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
                l.setType("2");
                l.setPassword("abc@123");
                l.setUsername(linkappTenantService.checkUsername());
                l.setModifyTime(new Date());
                l.setModifier(currentUser.getUsername());
                l.setCreateTime(new Date());
                l.setCreator(currentUser.getUsername());
                initModel(l);
                OperateArguments.add(l);
                validateModel(l); // 校验重复项
                String newestId = linkappUserMapper.selectNewestId();
                if(null != newestId){
                    l.setId(Long.parseLong(newestId)+1);
                    linkappUserMapper.insert(l);
                    // 保存角色
                    user2Roles(l,l.getRoles());
                }

            }
        });
    }

    //角色
    private List<LinkappRole> getRoles(String value, List<String> msgs, List<LinkappRole> roles) {
        List<LinkappRole> roleList = new ArrayList<>();
        List<String> roleNames = Arrays.asList(value.split(","));
        Map<String, LinkappRole> roleMap = roles.stream()
            .collect(Collectors.toMap(LinkappRole::getCode, p -> p));
        if(roleNames.isEmpty()){
            msgs.add("角色为空");
        }
        roleNames.forEach(p->{
            if (!roleMap.containsKey(p)){
                msgs.add("角色编码:【" + p +"】不存在为空");
            }else{
                roleList.add(roleMap.get(p));
            }
        });
        return roleList;
    }

    // 电话
    private String getPhone(String value, List<String> msgs, Set<String> phoneSet){
        String result = null;
        //手机号校验
        if (StringUtils.isEmpty(value)){
            msgs.add("手机号为空");
        }else if (phoneSet.contains(value)){
            msgs.add("与表格中单位数据重复");
        }else {
            phoneSet.add(value);
        }
        result = value;
        return result;
    }

    // 姓名
    private String getNickname(String value, List<String> msgs) {
        String result = null;
        if (StringUtils.isEmpty(value)){
            msgs.add("姓名为空");
        }else if (value.length() < 2){
            msgs.add("姓名长度小于2");
        }else if(value.length() > 32){
            msgs.add("姓名长度大于32");
        }
        result = value;
        return result;
    }

    // 性别
    private String getSex(String value, List<String> msgs) {
        String result = null;
        if (StringUtils.isEmpty(value)){
            msgs.add("性别为空");
        }else if (value.length() > 2){
            msgs.add("性别长度超过2个字符");
        }else if (!"男".equals(value)&&!"女".equals(value)){
            msgs.add("性别格式错误");
        }
        result = "男".equals(value) ? "1":"2";
        return result;
    }

    // 公司
    private String getAddress(String value, List<String> msgs) {
        String result = null;
        if (StringUtils.isEmpty(value)){
            msgs.add("公司为空");
        }else if (value.length() > 255){
            msgs.add("公司长度超过255个字符");
        }
        result = value;
        return result;
    }

}


